﻿using System;
using System.Collections.Generic;
using System.Linq;
using OS.Infrastructure.Domain;
using OS.Infrastructure.Domain.UnitOfWork;
using TaskKernel.Configuration;
using TaskKernel.DTO;
using TaskKernel.Domain;
using TaskKernel.Domain.Interface;
using TaskKernel.Domain.Services.Action;
using TaskKernel.Domain.Services.Mail;
using TaskKernel.Domain.Services.Permission;
using TaskKernel.Domain.Services.Role;

namespace TaskWebApplicationUI.PresenterLayer
{
    public class CreateTaskPresenter : AbstractPresenter<ICreateTaskView>
    {
        private readonly ICommandRepository<Task> _command;
        private readonly IMessageService _messageService;
        private readonly IQueryAccount _queryAccount;
        private readonly IQueryDepartment _queryDepartment;
        private readonly IQueryTask _queryTask;
        private readonly ITaskAction _taskAction;
        private readonly IUnitOfWorkFactory _uow;
        private Account _currentAccount;
        private Department _currentDepartment;

        public CreateTaskPresenter(IQueryDepartment queryDepartment, IQueryAccount queryAccount, IUnitOfWorkFactory uow,
                                   ITaskAction taskAction, IMessageService messageService, IQueryTask queryTask,
                                   ICommandRepository<Task> command)
        {
            _queryDepartment = queryDepartment;
            _queryAccount = queryAccount;
            _uow = uow;
            _taskAction = taskAction;
            _messageService = messageService;
            _queryTask = queryTask;
            _command = command;
        }

        #region Events

        private Account CurrentAccount
        {
            get
            {
                if (_currentAccount != null)
                    return _currentAccount;
                if (!String.IsNullOrEmpty(View.LogonUser)){
                    _currentAccount = _queryAccount.Get(View.LogonUser);
                    if (_currentAccount != null)
                        return _currentAccount;
                }
                throw new NotImplementedException("Don't know who are you...");
            }
        }

        private Department CurrentDepartment
        {
            get
            {
                if (_currentDepartment != null)
                    return _currentDepartment;
                if (!String.IsNullOrEmpty(View.WebPath)){
                    _currentDepartment = _queryDepartment.GetDepartmentByWebPath(View.WebPath);
                    if (_currentDepartment != null) return _currentDepartment;
                    if (_queryDepartment == null)
                        throw new NotImplementedException("queryDepartment is null");
                }
                throw new NotImplementedException("Don't know where are you...");
            }
        }

        private void SetDepartmentName()
        {
            View.DepartmentName = CurrentDepartment.DepartmentName;
        }

        public void OnViewInitialized()
        {
            using (var z = _uow.Create()){
                SetDepartmentName();
                LoadForm();
                LoadAccounts();
            }
        }

        private bool HasWaitingTasksToClose(Account account, Department department)
        {
            var tasks = _queryTask.WaitToCloseTasksOwnerInDepartment(account, department);
            return tasks.ToList().Count == 0;
        }

        private void LoadAccounts()
        {
            var accounts = _queryAccount.GetAllNotFiredAccounts();
            View.Users = accounts.ToViewWithEmptyValue();
        }

        private void VerifyUnclosedTask()
        {
            if (!HasWaitingTasksToClose(CurrentAccount, CurrentDepartment) && !CurrentDepartment.IsClaim)
                View.ErrorMessage = TaskKernelResources.UnclosedTaskExist;
        }

        public virtual void OnSave()
        {
            using (var z = _uow.Create()){
                var taskAction =
                    new TaskServiceByRoleInDepartment(_taskAction, CurrentDepartment, CurrentAccount).GetAction();
                var taskname = View.TaskName;
                if (!String.IsNullOrEmpty(taskname)){
                    if (!HasWaitingTasksToClose(CurrentAccount, CurrentDepartment) && !CurrentDepartment.IsClaim)
                        View.ErrorMessage = TaskKernelResources.UnclosedTaskExist;
                    else{
                        var task = new Task(CurrentAccount, CurrentDepartment, taskname);
                        _command.Save(task);
                        taskAction = new TaskServiceByRoleInTask(_taskAction, task, CurrentAccount).GetAction();
                        ComplexSave(task, taskAction);
                        _command.Save(task);
                        if (z.Commit()){
                            View.TaskID = task.UID;
                            View.RedirectWebPath = CurrentDepartment.WebPath;
                            _messageService.ChangeNotification(task, CurrentAccount, "Новая задача");
                        }
                    }
                }
                else
                    View.ErrorMessage = TaskKernelResources.RequiredField;
            }
        }

        public virtual void OnDelParticipant()
        {
            using (var z = _uow.Create()){
                var models = View.Participants;
                models.Remove(View.DeletedParticipant);
                View.Participants = models;
            }
        }

        public virtual void OnSearchParticipant()
        {
            using (var z = _uow.Create()){
                var word = View.UsersSearchString;
                if (!String.IsNullOrEmpty(word)){
                    var accounts = _queryAccount.GetAccountListBySearchWord(word);
                    View.Users = accounts.ToView();
                }
                else
                    LoadAccounts();
            }
        }

        public virtual void OnAddParticipant()
        {
            using (var z = _uow.Create()){
                var id = View.SelectedUser;
                if (id != Guid.Empty){
                    var participant = _queryAccount.Get(id);
                    if (participant != null){
                        var models = View.Participants;
                        var universalModel = new UniversalModelDDL(participant.UID, participant.FullName);
                        if (!models.Contains(universalModel)){
                            models.Add(universalModel);
                            View.Participants = models;
                        }
                    }
                }
            }
        }

        #endregion

        #region Load

        private void LoadForm()
        {
            LoadPagePermissions();
            LoadExecutors();
            LoadParticipants();
            LoadDepartments();
            VerifyUnclosedTask();
            View.Priority = (int) TaskPriority.Four;
        }

        private void LoadDepartments()
        {
            var departments = _queryDepartment.GetAllDepartments();
            if (departments.Count() > 0){
                View.Departments = departments.ToView();
                View.SelectedDepartment = CurrentDepartment.UID;
            }
        }

        private void LoadPagePermissions()
        {
            var roleService = new RoleInDepartment(CurrentDepartment, CurrentAccount);
            var permService = new DepartmentPermissions(roleService.GetRole(),
                                                        new IPermission<DepartmentPermissions>[]{
                                                                                                    new AdminExecutorCreateS(),
                                                                                                    new OtherCreateS()
                                                                                                });
            View.RolePermissions = permService.GetPermissions();
        }

        private void LoadParticipants()
        {
            View.Participants = new List<UniversalModelDDL>();
        }

        private void LoadExecutors()
        {
            var accountsDepartment = CurrentDepartment.Executors;
            if (accountsDepartment.Count() > 0){
                View.Executors = accountsDepartment.ToView();
                View.SelectedExecutor = Guid.Empty;
            }
        }

        #endregion

        #region Save

        private void ComplexSave(Task task, ITaskAction taskAction)
        {
            SaveHostName(task, taskAction);
            SaveParticipants(task, taskAction);
            SaveDepartmentParticipants(task, taskAction);
            SaveManagersAccountAsParticipant(task, taskAction);
            SaveExecutors(task, taskAction);
            SaveExpirationDate(task, taskAction);
            SavePriority(task, taskAction);
            SaveFiles(task, taskAction);
            SaveHistoryEntries(task, taskAction);
        }

        private void SaveManagersAccountAsParticipant(Task task, ITaskAction taskAction)
        {
            var managers = task.Owner.MasterAccounts;
            if (managers.Count() > 0)
                taskAction.AddParticipant(task, managers, CurrentAccount);
        }

        private void SaveDepartmentParticipants(Task task, ITaskAction taskAction)
        {
            var participants = CurrentDepartment.Participants;
            if (participants.Count() > 0)
                taskAction.AddParticipant(task, participants.ToList(), CurrentAccount);
        }

        private void SaveExpirationDate(Task task, ITaskAction taskAction)
        {
            if (View.ExpirationDate.HasValue)
                taskAction.SetExpirationDate(task, View.ExpirationDate.Value, CurrentAccount);
        }

        private void SavePriority(Task task, ITaskAction taskAction)
        {
            taskAction.SetPriority(task, (TaskPriority) View.Priority);
        }

        private void SaveHostName(Task task, ITaskAction taskAction)
        {
            var historyText = View.IPAddress;
            if (!String.IsNullOrEmpty(historyText))
                taskAction.AddComment(task, "IP: " + historyText, CurrentAccount);
        }

        private void SaveHistoryEntries(Task task, ITaskAction taskAction)
        {
            var historyText = View.HistoryEntryText;
            if (!String.IsNullOrEmpty(historyText)){
                historyText = historyText.Replace("</form>", "");
                historyText = historyText.Replace("<form", "");
                taskAction.AddComment(task, historyText, CurrentAccount);
            }
        }

        private void SaveParticipants(Task task, ITaskAction taskAction)
        {
            if (null != View.Participants){
                var newParticipants = AccountsModelToDomainModel(View.Participants);
                var delParticipants = new List<Account>();
                foreach (var participant in task.ParticipantsWithOwner.Select(x => x.Login)){
                    if (!newParticipants.Contains(participant))
                        delParticipants.Add(participant);
                    else
                        newParticipants.Remove(participant);
                }
                if (delParticipants.Count > 0)
                    taskAction.DeleteParticipant(task, delParticipants, CurrentAccount);
                if (newParticipants.Count > 0)
                    taskAction.AddParticipant(task, newParticipants, CurrentAccount);
            }
        }

        private void SaveExecutors(Task task, ITaskAction taskAction)
        {
            if (View.SelectedExecutor == Guid.Empty) taskAction.SetExecutor(task, null, CurrentAccount);
            else{
                var executor = _queryAccount.Get(View.SelectedExecutor);
                taskAction.SetExecutor(task, executor, CurrentAccount);
            }
        }

        private void SaveFiles(Task task, ITaskAction taskAction)
        {
            foreach (var uploadedFile in View.Filez)
                taskAction.AddFile(task, uploadedFile, CurrentAccount);
        }

        #endregion

        #region TransformationModel

        private IList<Account> AccountsModelToDomainModel(IList<UniversalModelDDL> models)
        {
            IList<Account> participants = new List<Account>();
            foreach (var view in models){
                var account = _queryAccount.Get(view.ID);
                if (null != account)
                    participants.Add(account);
            }
            return participants;
        }

        #endregion
    }
}